/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.ui;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;
import javax.swing.text.Keymap;
import javax.swing.undo.UndoManager;

import org.isqlviewer.swing.JTextPopup;
import org.isqlviewer.swing.SwingUtilities;
import org.isqlviewer.swing.action.CustomAction;
import org.isqlviewer.swing.action.SwingEventManager;
import org.isqlviewer.util.BasicUtilities;
import org.isqlviewer.util.LoggableObject;

/**
 * @author Mark A. Kobold &lt;mkobold at isqlviewer dot com&gt;
 * @version 1.0
 */
public abstract class AbstractApplicationView extends LoggableObject implements ApplicationView {

    private static final Insets buttonInsets = SwingUtilities.isMacOS() ? new Insets(2, 3, 2, 3) : new Insets(
            1, 1, 1, 1);
    private static final JTextPopup textPopup = new JTextPopup();

    private static final GridBagConstraints UI_CONSTRAINT = new GridBagConstraints(0, 0, 0, 0, 0, 0, 0, 0, new Insets(
            1, 1, 1, 1), 0, 0);

    protected JMenu createMenu(String name) {

        StringBuilder title = new StringBuilder(name);
        int mnemonicIdx = name.indexOf('&');
        char mnemonic = 0;

        if (mnemonicIdx > 1) {
            // check to see if it is escaped ?
            char previous = name.charAt(mnemonicIdx - 1);
            char next = mnemonicIdx == name.length() ? 0 : name.charAt(mnemonicIdx + 1);
            if (previous != '&') {
                mnemonic = next;
                title.delete(mnemonicIdx - 1, mnemonicIdx);
            }
        } else if (mnemonicIdx == 0) {
            char next = mnemonicIdx == name.length() ? 0 : name.charAt(mnemonicIdx + 1);
            if (next != '&') {
                title.delete(mnemonicIdx, mnemonicIdx + 1);
                mnemonic = next;
            }
        }

        JMenu menu = new JMenu(title.toString());
        if (mnemonic > 0) {
            menu.setMnemonic(mnemonic);
        }
        return menu;
    }

    protected void addMenuItem(JMenu menu, Action action) {

        if (action == null)
            return;
        JMenuItem menuItem = menu.add(action);
        configureMenuItem(menuItem, action);
    }

    protected void addRadioButtonMenuItem(JMenu menu, ButtonGroup group, Action a) {

        addRadioButtonMenuItem(menu, group, a, false);
    }

    protected void addRadioButtonMenuItem(JMenu menu, ButtonGroup group, Action a, boolean selected) {

        JRadioButtonMenuItem mi = new JRadioButtonMenuItem(a);
        mi.setSelected(selected);
        menu.add(mi);
        group.add(mi);
        configureMenuItem(mi, a);
        mi.setIcon(null);
    }

    protected void addDynamicGroupAction(JMenu menu, String icon, ButtonGroup group, int key, String text) {

        CustomAction action = new CustomAction(text, key, null);
        action.putValue(Action.NAME, text);
        action.putValue(Action.DEFAULT, text);
        action.putValue(CustomAction.ICON_NAME, icon);
        addRadioButtonMenuItem(menu, group, action);
    }

    protected void addDynamicAction(JMenu menu, SwingEventManager manager, String icon, int key, String text) {

        CustomAction action = new CustomAction(text, key, manager);
        action.putValue(Action.NAME, text);
        action.putValue(Action.DEFAULT, text);
        action.putValue(Action.ACTION_COMMAND_KEY, text);
        action.putValue(CustomAction.ICON_NAME, icon);
        if ((menu.getMenuComponentCount() >= 0) && (menu.getMenuComponentCount() <= 9)) {
            char code = Character.forDigit(menu.getMenuComponentCount(), 10);
            KeyStroke ks = KeyStroke.getKeyStroke(code, BasicUtilities.getCommandMask(), true);
            action.putValue(Action.ACCELERATOR_KEY, ks);
        }
        addMenuItem(menu, action);
    }

    protected void configureMenuItem(JMenuItem menuItem, Action action) {

        String value = null;
        KeyStroke ks = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY);
        if (ks != null) {
            menuItem.setAccelerator(ks);
        }

        value = (String) action.getValue(Action.DEFAULT);
        if (value != null) {
            menuItem.setText(value);
        } else {
            menuItem.setText("<!--NULL--!>");
            menuItem.setForeground(Color.RED);
        }
        value = (String) action.getValue(CustomAction.ICON_NAME);
        if (value != null) {
            menuItem.setIcon(SwingUtilities.loadIconResource(value, 16));
        } else {
            menuItem.setIcon((SwingUtilities.loadIconResource("spacer", 16)));
        }
    }

    protected void addButton(JToolBar toolbar, SwingEventManager manager, Action action) {

        if (action == null || toolbar == null) {
            return;
        }
        JButton button = toolbar.add(action);
        configureButton(manager, button, action);
    }

    /**
     * Helper method to configure the button
     */
    protected void configureButton(SwingEventManager manager, AbstractButton button, Action action) {

        if (action != null) {
            Object actionID = action.getValue(CustomAction.ACTION_ID);
            button.setActionCommand(actionID == null ? "-1" : actionID.toString());
            button.setText("");
            String s = (String) action.getValue(CustomAction.ICON_NAME);
            button.setIcon(SwingUtilities.loadIconResource(s, 16));
        }

        button.setAlignmentY(0.5f);
        button.setBorderPainted(!SwingUtilities.isMacOS());
        button.setFocusPainted(false);
        button.setMargin(buttonInsets);
        if (manager != null) {
            button.addMouseListener(manager);
        }
    }

    protected static final void unlocalizeTextComponent(JTextComponent txt, UndoManager mgr) {

        textPopup.removeJTextComponent(txt);
        if (mgr != null) {
            Keymap map = txt.getKeymap();
            KeyStroke ks = null;

            ks = SwingUtilities.createKeyStroke(KeyEvent.VK_Z, SwingUtilities.MENU_SHORTCUT_MASK);
            map.removeKeyStrokeBinding(ks);
            ks = SwingUtilities.createKeyStroke(KeyEvent.VK_Y, SwingUtilities.MENU_SHORTCUT_MASK);
            map.removeKeyStrokeBinding(ks);
        }
    }

    /**
     * Ensures the editor kit for the given text component is using standard clipboard accelerators.
     * <p>
     * This will also install the {ctrl|command}-[Y-Z] if the given undo manager is null.
     * 
     * @param txt to localize.
     * @param mgr if undo support is enabled for this txt component.
     */
    protected static final void localizeTextComponent(JTextComponent txt, UndoManager mgr) {

        ActionMap am = txt.getActionMap();
        Keymap map = txt.getKeymap();
        Action a = null;
        KeyStroke ks = null;

        a = am.get(DefaultEditorKit.cutAction);
        ks = SwingUtilities.createKeyStroke(KeyEvent.VK_X, SwingUtilities.MENU_SHORTCUT_MASK);
        map.addActionForKeyStroke(ks, a);
        a = am.get(DefaultEditorKit.copyAction);
        ks = SwingUtilities.createKeyStroke(KeyEvent.VK_C, SwingUtilities.MENU_SHORTCUT_MASK);
        map.addActionForKeyStroke(ks, a);
        a = am.get(DefaultEditorKit.pasteAction);
        ks = SwingUtilities.createKeyStroke(KeyEvent.VK_V, SwingUtilities.MENU_SHORTCUT_MASK);
        map.addActionForKeyStroke(ks, a);
        a = am.get(DefaultEditorKit.selectAllAction);
        ks = SwingUtilities.createKeyStroke(KeyEvent.VK_A, SwingUtilities.MENU_SHORTCUT_MASK);
        map.addActionForKeyStroke(ks, a);

        textPopup.addJTextComponent(txt);
        if (mgr != null) {
            a = new UndoTextAction(mgr);
            ks = SwingUtilities.createKeyStroke(KeyEvent.VK_Z, SwingUtilities.MENU_SHORTCUT_MASK);
            map.addActionForKeyStroke(ks, a);
            a = new RedoTextAction(mgr);
            ks = SwingUtilities.createKeyStroke(KeyEvent.VK_Y, SwingUtilities.MENU_SHORTCUT_MASK);
            map.addActionForKeyStroke(ks, a);
        }
    }

    protected static GridBagConstraints constrain(int x, int y, int w, int h, double wx, double wy, int a, int f) {

        UI_CONSTRAINT.gridx = x;
        UI_CONSTRAINT.gridy = y;
        UI_CONSTRAINT.gridwidth = w;
        UI_CONSTRAINT.gridheight = h;
        UI_CONSTRAINT.weightx = wx;
        UI_CONSTRAINT.weighty = wy;
        UI_CONSTRAINT.anchor = a;
        UI_CONSTRAINT.fill = f;
        return UI_CONSTRAINT;
    }

    public static class UndoTextAction extends AbstractAction {

        private static final long serialVersionUID = 1L;
        private UndoManager um = null;

        public UndoTextAction(UndoManager mgr) {

            um = mgr;
        }

        public void actionPerformed(ActionEvent e) {

            try {
                um.undo();
            } catch (Throwable t) {
                // beep();
            }
        }
    }

    public static class RedoTextAction extends AbstractAction {

        private static final long serialVersionUID = 1L;
        private UndoManager um = null;

        public RedoTextAction(UndoManager mgr) {

            um = mgr;
        }

        public void actionPerformed(ActionEvent e) {

            try {
                um.redo();
            } catch (Throwable t) {
                // beep();
            }
        }
    }
}
